package storage

import (
	"math"
	"net"
	"reflect"
	"time"
	"unsafe"

	"github.com/vmihailenco/msgpack/v5"
)

// Entry 缓存项数据副本
type Entry struct {
	Key    string        // 缓存键名
	Kind   Kind          // 缓存类型
	Value  any           // 缓存值, 序列化缓存数据
	Size   int           // 大小，单位为字节
	Expire time.Duration // 过期时间, 0 表示不过期
	Tags   []string      // 标签列表
}

const (
	kindIntOverhead       = int(unsafe.Sizeof(int(0)))
	kindUintOverhead      = int(unsafe.Sizeof(uint(0)))
	kindTimeOverhead      = int(unsafe.Sizeof(time.Time{}))
	kindIPOverhead        = int(unsafe.Sizeof(net.IPv4zero))
	kindBytesOverhead     = 16                          // []byte 占用空间为16字节
	kindSetStringOverhead = 128 + 144 + (144 * 5 / 100) // 144*0.05
	kindSetIntOverhead    = 128 + 80 + (80 * 5 / 100)   // 80*0.05
)

// EntryValueSize 获取指定kind类型的val值占用的字节数，主要用于估算缓存占用空间
//   - val 为 EntryValueUnderef 已预处理过指针脱敏后的基本标量类型
//   - 注意: 该方法仅适用于基本类型，对于序列化存储的类型，返回值为0
func EntryValueSize(kind Kind, key string, val any) int {
	size := len(key) + 16 // key 占用空间为 16 + len 字节
	if val == nil {
		return size
	}
	switch kind {
	case KindNil:
		// 0
	case KindString:
		if s, ok := val.(string); ok {
			size += len(s) + 16
		} else {
			size += 16
		}
	case KindBool:
		size += 1
	case KindInt64:
		size += 8
	case KindInt32:
		size += 4
	case KindInt16:
		size += 2
	case KindInt8:
		size += 1
	case KindInt:
		size += kindIntOverhead
	case KindUint64:
		size += 8
	case KindUint32:
		size += 4
	case KindUint16:
		size += 2
	case KindUint8:
		size += 1
	case KindUint:
		size += kindUintOverhead
	case KindFloat64:
		size += 8
	case KindFloat32:
		size += 4
	case KindTime:
		size += kindTimeOverhead
	case KindDuration:
		size += 8
	case KindWeekday:
		size += 4
	case KindMonth:
		size += 4
	case KindComplex64:
		size += 8 // complex64 占用空间为8字节
	case KindComplex128:
		size += 16 // complex128 占用空间为16字节
	case KindIP:
		if ip, ok := val.(net.IP); ok {
			size += len(ip)
		} else {
			size += kindIPOverhead
		}
	case KindBytes:
		if b, ok := val.([]byte); ok {
			size += len(b)
		} else {
			size += kindBytesOverhead
		}
	case KindSetString:
		// 字符串集合类型
		// 在AMD64架构下，键为string、值为空struct{}的map，每个键值对实际占用约24字节
		// string类型作为键时存储的是其运行时实现，固定16字节
		// 空struct{}不额外占内存，但map的哈希表本身会对键值对整体按8字节对齐
		// 桶（bucket）的存储结构为数组，每个桶默认存储8个键值对，共 8（tophash） +  8*16（键） + 0（值） + 8（指针） = 144字节
		// 因此需要预估桶数量和键值对数量
		// ≈ Σ(字符串键长度 + 16) + 128 + (桶数量 × 144) +  (桶数量 × 144 × 5%)
		if val, ok := val.(map[string]struct{}); ok {
			bucketCount := mapBucketsEstimate(len(val))
			for key := range val {
				size += len(key) + 16
			}
			size = size + 128 + bucketCount*144 + (bucketCount * 144 * 5 / 100)
		} else {
			size += kindSetStringOverhead // 128 + 144 + (144 * 5 / 100)
		}

	case KindSetInt:
		// 整型集合类型
		// 在AMD64架构下，键为int64、值为空struct{}的map，每个键值对实际占用约24字节
		// int64 类型作为键时存储的是其运行时实现，固定8字节
		// 空struct{}不额外占内存，但map的哈希表本身会对键值对整体按8字节对齐
		// 桶（bucket）的存储结构为数组，每个桶默认存储8个键值对，共 8（tophash） + 64（键） + 0（值） + 8（指针） = 80字节
		// 因此需要预估桶数量和键值对数量
		// ≈ 128 + (桶数量 × 80) + (桶数量 × 80 × 5%)
		if val, ok := val.(map[int64]struct{}); ok {
			bucketCount := mapBucketsEstimate(len(val))
			size = size + 128 + (bucketCount * 80) + (bucketCount * 80 * 5 / 100)
		} else {
			size += kindSetIntOverhead //128 + 80 + (80 * 5 / 100)
		}
	default:
		// KindSerializer 及其他类型，序列化存储
		switch val := val.(type) {
		case []byte:
			size += len(val)
		default:
			// 其他类型，序列化存储
			buf, _ := msgpack.Marshal(val)
			size += len(buf)
		}
	}
	return size
}

// map 扩容负载因子，用于估算map桶的数量
const mapLoadFactor float64 = 6.5

// mapBucketsEstimate 估算 map 的桶数量，用于估算缓存占用空间
func mapBucketsEstimate(n int) int {
	bucketCount := int(math.Ceil(float64(n) / mapLoadFactor))
	B := 0
	for (1 << B) < bucketCount {
		B++
	}
	return 1 << B
}

// EntryValueUnderef 探测 any 目标, 将指针类型脱敏为原始标量类型
//   - 如果目标是nil, 则返回nil
//   - 如果目标非指针, 则返回原值
//   - 如果目标是nil指针, 则返回nil
//   - 如果目标是非nil指针, 则返回指针指向的指向的值(any类型的原始标量)
func EntryValueUnderef(v any) any {
	if v == nil {
		return nil
	}
	val := reflect.ValueOf(v)
	if !val.IsValid() || val.Kind() != reflect.Ptr {
		return v
	}
	// 脱敏指针类型, 直到非指针为止
	if val.Kind() == reflect.Ptr {
		if val.IsNil() {
			return nil
		}
		// 脱敏指针类型，防止循环引用
		const maxDepth = 100
		for i := 0; i < maxDepth && val.Kind() == reflect.Ptr; i++ {
			if val.IsNil() {
				return nil
			}
			val = val.Elem()
		}
	}
	switch val.Kind() {
	// reflect.Pointer, // Pointer == Ptr
	case reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer,
		reflect.Interface, reflect.Slice:
		if val.IsNil() {
			return nil
		}
	}
	if !val.CanInterface() {
		return nil
	}
	return val.Interface()
}
